%%% Copyright 2010 Google Inc.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%%     http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.

%%% Author jacoby@google.com (Jacoby Thwaites)
%%% Header file for TXM. Includes record definitions and standard
%%% constants.

%%% ---------------------------------------------------------------------------
%%% STANDARD PROCESS VALUES
%%% ---------------------------------------------------------------------------

%% Gives all processes 5 seconds to shutdown (this is better than
%% brutal_kill because the terminate() function is called back on
%% most behaviours).
-define(SHUTDOWN_TIME_FLAG, shutdown_time).
-define(SHUTDOWN_TIME, 5000).

%% All servers respond to this event or call name.
-define(STOP, stop).

%% Time to wait for Mnesia tables to open
-define(OPEN_TIMEOUT_FLAG, open_timeout).
-define(OPEN_TIMEOUT, 10000).

%% Time to wait for server calls to complete.
-define(SYNC_CALL_TIMEOUT, infinity).

%%% ---------------------------------------------------------------------------
%%% STANDARD RETURN VALUES
%%% ---------------------------------------------------------------------------

%% Process normal exit
-define(NORMAL_EXIT, normal).

%% Function normal return with zero or more args.
%% @todo(jacoby) Change so always have 2 arg return, 2nd arg can be tuple.
-define(OK_RETURN, ok).
-define(OK_RETURN(V), {ok, V}).
-define(OK_RETURN(V1, V2), {ok, V1, V2}).

%% Macro to match {ok, Value} and return unadorned Value.
%% This is for use inside expressions such as list comprehensions.
-define(OK_RESULT(Expr), (fun() -> ?OK_RETURN(Result) = Expr, Result end)()).

%% Function error return for functions that don't always succeed
-define(ERROR_RETURN, error).
-define(ERROR_RETURN(Reason), {error, Reason}).

%%% ---------------------------------------------------------------------------
%%% TXM APP AND LOGGING CONSTANTS
%%% ---------------------------------------------------------------------------

%% TXM application name (see txm.app)
-define(TXM_APP, txm).

%% Startup mode environment variable name and values (see txm.app).
-define(ENV_STARTUP, startup).
-define(STARTUP_LOCAL, local).

%% Optional startup items environment variable name (see txm.app).
-define(ENV_STARTUP_OPTIONS, options).

%% Type values for error_logger: reports.
%% See [http://www.erlang.org/doc/man/error_logger.html]
-define(TXM_LOG, txm).

%% Log level environment variable name and values (see txm.app).
-define(ENV_LOG_LEVEL, log_level).
-define(LOG_LEVEL_DEBUG, 0).
-define(LOG_LEVEL_INFO, 1).
-define(LOG_LEVEL_WARNING, 2).
-define(LOG_LEVEL_ERROR, 3).

%% Web callback timeout environment variable name (see txm.app).
-define(ENV_WEB_CALLBACK_TIMEOUT_SECS, web_timeout_secs).

%% Log macros. These record the module and line number automatically.
%% The argument must be a property list, i.e. list(atom() | {atom(), term()})
%% Example 1:
%%   ?INFO([{init, Impl}, {args, Args}])
%%
%% Example 2:
%%   ?INFO([shutdown])
-ifdef(debug).
  -define(DEBUG(ReportList), txm_log:debug(?MODULE, ?LINE, ReportList)).
-else.
  -define(DEBUG(ReportList), ?OK_RETURN).
-endif.
-define(INFO(ReportList), txm_log:info(?MODULE, ?LINE, ReportList)).
-define(WARNING(ReportList), txm_log:warning(?MODULE, ?LINE, ReportList)).
-define(ERROR(ReportList), txm_log:error(?MODULE, ?LINE, ReportList)).

%%% ---------------------------------------------------------------------------
%%% STANDARD ERROR REASONS
%%% ---------------------------------------------------------------------------
-define(REASON_NOT_FOUND, not_found).
-define(REASON_DUPLICATE, duplicate).
-define(REASON_WRONG_KIND, wrong_kind).
-define(REASON_WRONG_STATE, wrong_state).
-define(REASON_NO_VERSION, no_version).
-define(REASON_NOT_IMPLEMENTED, not_implemented).

%%% ---------------------------------------------------------------------------
%%% STANDARD CONSTANTS
%%% ---------------------------------------------------------------------------
-define(ROOT_FOLDER_NAME, "/").
-define(PATH_SEPARATOR, "/").
-define(PARENT_FOLDER_NAME, "..").
-define(NAME_SEPARATOR, " ").

%%% ---------------------------------------------------------------------------
%%% STANDARD DEFAULT RECORD VALUES AND STRING EQUIVALENT WHERE NECESSARY
%%% ---------------------------------------------------------------------------

%% A list, initially empty. An empty string is identical, of course, but hey.
-define(EMPTY, []).
-define(EMPTY_STRING, "").

%% A null value
-define(NULL, null).

%% An undefined value. All uninitialized record fields have this value.
-define(UNDEFINED, undefined).

%% Record default values used in preference to 'undefined'
-define(NULL_ID, {-1, -1, -1}).
-define(NULL_REF, {op, ?NULL_ID}).
-define(NULL_PID, list_to_pid("<0.0.0>")).

%% Operation types
-define(OP_TYPE_N, notification).
-define(OP_TYPE_OO, one_way).
-define(OP_TYPE_RR, request_reply).
-define(OP_TYPE_IRR, iterative_request_reply).
-define(OP_TYPE_NULL, null).

%%% ---------------------------------------------------------------------------
%%% TYPE DEFINITIONS
%%% ---------------------------------------------------------------------------
%% @type id() = {integer(), integer(), integer()}.
%% A unique primary key for records of all types. This means there is a flat
%% keyspace for every record in the system, whatever table it is in.
-type(id() :: {integer(), integer(), integer()}).

%% @type time() = {integer(), integer(), integer()}.
%% Time reference, used in view_fsm cleaner process for example.
-type(time() :: {integer(), integer(), integer()}).

%% @type refId() = id().
%% A reference to another record's primary key in a known or implicit table.
-type(refId() :: id()).

%% @type ref() = {(op | field | folder), refId()}.
%% A reference to another record's primary key in a specified table.
-type(ref() :: {(op | field | folder), refId()}).

%% @type opInstanceState() = static | pending | complete.
%% An operation instance can be in one of these states:
%% <ul>
%% <li>`static' = permanently expecting event collection</li>
%% <li>`pending' = expecting a one-off event collection</li>
%% <li>`complete' = have completed collection</li>
%% </ul>
-type(opInstanceState() :: static | pending | complete).

%% @type name() = string().
%% A name or alias, which must be a string, not an atom.
-type(name() :: string()).

%% @type pathname() = string().
%% A pathname with `/' separated parts, which must be a string, not an atom.
-type(path() :: string()).

%% @type attribute() = {name(), value()}. An attribute is a key/value
%% pair where the value is a simple string, boolean or number.
-type(attribute() :: {name(), term()}).

%% @type struct() = {name()}
%%                | {name(), list(attribute())}
%%                | {name(), list(attribute()), list(struct())}.
%% A struct can be minimal (resulting in just `<name/>' or `name : true', for
%% example), simple (a property with simple attributes) or complex (a
%% recursive definition where the content of the `struct' is a list
%% of `struct').
-type(struct() :: {name()}
                | {name() , list(attribute())}
                | {name(), list(attribute()), list(struct())}).


%% @type primitiveType() = atom().
%% A primitive field type such as `integer', `string'.
-type(primitiveType() :: atom()).

%% @type complexType() = string().
%% A complex type is in the form of a URL including path which
%% allows definition of sub-types.
-type(complexType() :: string()).

%% @type reason() = atom().
%% An error reason is an atom returned in an error tuple, for those
%% functions where an error return is specified.
-type(reason() :: atom()).

%% nr_set and nr_set_dict definitions
-define(NR_SET, '$nr_set').
-define(NOT_FOUND, '$not_found').

-record(?NR_SET, {n2r = dict:new(),
                  r2n = dict:new(),
                  refSet = sets:new()}).

-type(nr_set() :: #?NR_SET{}).
-type(nr_entry() :: {string(), ref()}).
-type(not_found() :: '$not_found').

-define(NR_SET_DICT, '$nr_set_dict').

-record(?NR_SET_DICT, {dict = dict:new(),
                       refSet = sets:new()}).
-type(nr_set_dict() :: #?NR_SET_DICT{}).

%%@type opType() = n | oo | rr | irr.
%% An op can be one of:
%% <ul>
%% <li>`n' - Notification (output only)</li>
%% <li>`oo' - One-way (input only)</li>
%% <li>`rr' - Request/Reply (non-intersecting input and output)</li>
%% <li>`irr' - Iterative Request/Reply (intersecting input and output)</li>
%% <li>`null' - Null (no input or output)</li>
%% </ul>
-type(opType() :: ?OP_TYPE_N
                | ?OP_TYPE_OO
                | ?OP_TYPE_RR
                | ?OP_TYPE_IRR
                | ?OP_TYPE_NULL).

%%% ---------------------------------------------------------------------------
%%% PROGRAM OPERATIONS, PROPERTIES, FIELDS AND FOLDERS
%%% ---------------------------------------------------------------------------

%% Individual tables matching record types, and list thereof.
-define(TABLES, [?OP, ?FOLDER, ?FIELD]).

%% Root folder ID, which must match the id pattern below.
-define(ROOT_FOLDER_ID, {0, 0, 0}).

%% Root folder reference.
-define(ROOT_FOLDER_REF, {?FOLDER, ?ROOT_FOLDER_ID}).

%% Pattern to match ID and REF arguments.
-define(ID_PTN, {_, _, _}).
-define(REF_PTN, {_, ?ID_PTN}).

%% @type op() = #op{}.
%% An operation is a function with input and/or outputs. The event machine
%% acts to sequence events which are data sent to or received from operations.
%%
%% The `rootFolderId' field holds the mutable tree, modified by functions in
%% the `prog' module. When the snapshot/0 function is called, a copy of
%% the tree is made and placed as a new entry in the `versions' dictionary.
%%
%% Processes in the machine only operate on snapshotted versions.
%% ===Fields===
%% `id =' {@type id()}<p/>
%% `mask = ' {@type nr_set()}<p/>
%% `input =' {@type nr_set()}<p/>
%% `outputs =' {@type nr_set_dict()}<p/>
%% `props =' {@type list()}, a proplist<p/>
%% `isComposite =' {@type boolean()}, if true then a root folder exists<p/>
%% `rootFolderRef =' {@type ref()}, a reference to the folder<p/>
%% `versions = ' {@type list(version())}<p/>
%% `opType = ' {@type opType()}<p/>
%%
%% A composite operation has a root folder, and an initially empty list of
%% `versions' where each element is {integer(), name(), ref()}, in reverse
%% order of snapshotting (i.e. first element is newest).
%%
%% An operation's type determines its role in sequencing.
-define(OP, op).
-record(?OP, {
  id = ?NULL_ID               :: id(),
  mask = nr_set:new()         :: nr_set(),
  input = nr_set:new()        :: nr_set(),
  outputs = nr_set_dict:new() :: nr_set_dict(),
  props = ?EMPTY              :: list(),
  isComposite = false         :: boolean(),
  rootFolderRef = ?NULL_REF   :: ref(),
  versions = ?EMPTY           :: list(),
  opType = ?OP_TYPE_NULL      :: opType()
}).
-type(op() :: #?OP{}).

%% @type version() = {integer(), string(), ref()}. An operation contains
%% a list of version tuples, which can be empty.
%%
%% Each tuple has a version number, a string name (which can be empty)
%% and a reference to the folder that is the root of that version.
-type(version() :: {integer(), string(), ref()}).

%% @type versionRef() = integer() | string() | latest | dynamic. A version
%% is identified by number, can have an optional label/name, and can
%% be referred to by either of those or the atom `latest' or `dynamic'
%% both of which return the latest version.
-define(VERSION_LATEST, latest).
-define(VERSION_DYNAMIC, dynamic).
-type(versionref() :: integer()
                    | string()
                    | ?VERSION_DYNAMIC
                    | ?VERSION_LATEST).

%% A folder has a name and contains entries each of which has a unique
%% name. Each entry can be a folder, field or operation.
%% ===Fields===
%% `id = ' {@type id()}<p/>
%% `entries = '{@type nr_set()}
%% The folder whose id is ?ROOT_FOLDER_ID is the root of the whole tree. Its
%% name must be ?ROOT_FOLDER_NAME.
-define(FOLDER, folder).
-record(?FOLDER, {
  id = ?NULL_ID          :: id(),
  entries = nr_set:new() :: nr_set()
}).
-type(folder() :: #?FOLDER{}).

%% @type field() = #field{}.
%% A field has a type, and is referenced by operation input and
%% output sets.
%% ===Fields===
%% `id =' {@type id()}<p/>
%% `type =' {@type primitiveType()} | {@type complexType()}
-define(FIELD, field).
-record(?FIELD, {
  id = ?NULL_ID  :: id(),
  type = ?NULL   :: primitiveType() | complexType()
}).
-type(field() :: #?FIELD{}).

%% @type rec() = field() | folder() | op().
%% A record is either a field, an operation or a folder
-type(rec() :: field() | folder() | op()).
